package com.sfpay.coreplatform.account.service.impl;

import java.util.List;

import javax.annotation.Resource;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import com.sfpay.coreplatform.account.common.AccountResponseCode;
import com.sfpay.coreplatform.account.common.enums.AccountActive;
import com.sfpay.coreplatform.account.common.enums.AccountState;
import com.sfpay.coreplatform.account.common.enums.AccountType;
import com.sfpay.coreplatform.account.common.enums.DayChangeEnums.AccountingDay;
import com.sfpay.coreplatform.account.common.enums.DrcrFlag;
import com.sfpay.coreplatform.account.common.enums.FreezeState;
import com.sfpay.coreplatform.account.common.enums.PasswordFlag;
import com.sfpay.coreplatform.account.persistence.dao.IAccountDao;
import com.sfpay.coreplatform.account.persistence.dao.ITallySerialDao;
import com.sfpay.coreplatform.account.service.ITransferService;
import com.sfpay.coreplatform.account.service.exception.AccountServiceException;
import com.sfpay.coreplatform.account.service.inner.IAccountPostingRuleService;
import com.sfpay.coreplatform.account.service.inner.IAsyncAccountService;
import com.sfpay.coreplatform.account.service.inner.IDayChangeService;
import com.sfpay.coreplatform.account.valueobject.dto.Transfer;
import com.sfpay.coreplatform.account.valueobject.tmo.AccountVO;
import com.sfpay.coreplatform.account.valueobject.tmo.TallySerial;
import com.sfpay.framework.common.util.AssertUtils;

@Service
public class TransferServiceImpl implements ITransferService {
	
	private static Logger logger = LoggerFactory.getLogger(TransferServiceImpl.class);
	
	@Resource
	private ITallySerialDao tallySerialDao;

	@Resource
	private IAccountDao accountDao;

	@Resource
	private IDayChangeService dayChangeService;

	@Resource
	private IAsyncAccountService asyncAccount;

	@Resource
	private IAccountPostingRuleService accountPostingRuleService;
	
	@Override
	@Transactional(rollbackFor=AccountServiceException.class)
	public void transfer(Transfer transfer)
			throws AccountServiceException {
		
		logger.debug("transfer(Transfer transfer=" + transfer + ") - start"); 
		validatorParam(transfer);
		accountValidator(transfer, true);
		doTransfer(transfer);
		logger.debug("transfer(Transfer) - end"); //$NON-NLS-1$
		
	}

	@Override
	@Transactional(rollbackFor=AccountServiceException.class)
	public void transferWithoutPsw(Transfer transfer)
			throws AccountServiceException {
		
		logger.debug("transferWithoutPsw(Transfer transfer=" + transfer + ") - start"); 
		validatorParam(transfer);
		accountValidator(transfer, false);
		doTransfer(transfer);
		logger.debug("transferWithoutPsw(Transfer) - end"); 
		
	}
	
	
	private void doTransfer(Transfer transfer) throws AccountServiceException {

		// 判断付款方账户是否异步记账
		
		try{
			boolean isPayerAsync = asyncAccount.isAsyncAccount(transfer
					.getPayerAccount());
			// boolean isPayerAsync = false;
			// 判断收款方账户是否异步记账
			boolean isPayeeAsync = asyncAccount.isAsyncAccount(transfer
					.getPayeeAccount());

			Long prePayerBalanceAmount = 0L;
			Long prePayeeBalanceAmount = 0L;
			Long payerBalanceAmount = 0L;
			Long payeeBalanceAmount = 0L;
			if (isPayerAsync == false) {
				AccountVO payerAccountVO = accountDao
						.selectByAccountNoAndLocked(transfer.getPayerAccount());
				logger.info("lock account " + transfer.getPayerAccount());
				prePayerBalanceAmount = payerAccountVO.getCashAmount();
				payerBalanceAmount = calculateBalance(payerAccountVO,
						transfer.getAmount(), DrcrFlag.DR);
				payerAccountVO.setCashAmount(payerBalanceAmount);
				accountDao.updateByPrimaryKey(payerAccountVO);
			}

			if (isPayeeAsync == false) {
				AccountVO payeeAccountVO = accountDao
						.selectByAccountNoAndLocked(transfer.getPayeeAccount());
				logger.info("lock account " + transfer.getPayeeAccount());
				prePayeeBalanceAmount = payeeAccountVO.getCashAmount();
				payeeBalanceAmount = calculateBalance(payeeAccountVO,
						transfer.getAmount(), DrcrFlag.CR);
				payeeAccountVO.setCashAmount(payeeBalanceAmount);
				accountDao.updateByPrimaryKey(payeeAccountVO);
			}
			List<TallySerial> list = accountPostingRuleService
					.generateTallySerials(transfer, prePayerBalanceAmount,
							prePayeeBalanceAmount, payerBalanceAmount,
							payeeBalanceAmount, isPayerAsync, isPayeeAsync);
			if (logger.isDebugEnabled()) {
				logger.debug("AccountPostingServiceImpl_processAccount generateTallySerials list size:"
						+ list.size());
			}
			String accountDate = dayChangeService.findDate(AccountingDay.CUR_DAY)
					.getValue();
			for (TallySerial tallySerial : list) {
				// 保存记账流水
				tallySerial.setTallyDate(accountDate);
				tallySerialDao.addTallySerial(tallySerial);
			}
		}catch(AccountServiceException ase){
			throw ase;
		}catch(Exception e){
			logger.error("AccountPostingServiceImpl_processAccount exception:",e);
			throw new AccountServiceException(AccountResponseCode.ACCT_POSTING_EXCEPTION,"AccountPostingServiceImpl_processAccount exception:payerAccount["+transfer.getPayerAccount()+"]"+",payeeAccount["+transfer.getPayeeAccount()+"]",e);
		
		}
		

	}
	
	
	
	
	
	
	/**
	 * 计算账户余额
	 * @param accountVO
	 * @param drCrAmount
	 * @param drcrFlag
	 * @return
	 */
	private Long calculateBalance(AccountVO accountVO, Long drCrAmount,
			DrcrFlag drcrFlag) {
		Long balanceAmount = 0L;
		if (drcrFlag == accountVO.getSubjectDrCr()) {
			balanceAmount = accountVO.getCashAmount() + drCrAmount;
		} else {
			balanceAmount = accountVO.getCashAmount() - drCrAmount;
		}
		return balanceAmount;
	}
	
	
	/**
	 * 账户校验
	 *
	 * 方法说明：
	 *
	 * @param transfer
	 *
	 * @author 299015 彭阳
	 * @throws AccountServiceException 
	 */
	private void accountValidator(Transfer transfer, boolean checkPsw) throws AccountServiceException {
		logger.debug("validate payer accountNo and payee accountNo");
		if (transfer.getPayeeAccount().equals(transfer.getPayerAccount())) {
			throw new AccountServiceException(
					AccountResponseCode.ACCOUNT_ACCOUNTNO_EQUAL_ERROR,
					"payer account and  payee acount can not be same");
		}

		AccountVO payerAccountVO = accountDao.selectByAccountNo(transfer
				.getPayerAccount());
		if (payerAccountVO == null)
			throw new AccountServiceException(
					AccountResponseCode.ACCOUNT_ACCOUNTNO_NOT_EXIST_ERROR,
					"payer accountNo is not exist");
		accoutStatusVValidator(payerAccountVO, DrcrFlag.DR);
		AccountVO payeeAccountVO = accountDao.selectByAccountNo(transfer
				.getPayeeAccount());
		if (payeeAccountVO == null)
			throw new AccountServiceException(
					AccountResponseCode.ACCOUNT_ACCOUNTNO_NOT_EXIST_ERROR,
					"payee accountNo is not exist");
		accoutStatusVValidator(payeeAccountVO, DrcrFlag.CR);

		if (transfer.getPayeeAccount().equals(transfer.getPayerAccount())) {
			throw new AccountServiceException(
					AccountResponseCode.ACCOUNT_ACCOUNTNO_EQUAL_ERROR,
					"payer account and  payee acount can not be same");
		}
		// 只校验会员账户
		if (AccountType.MEMBER_ACC == payerAccountVO.getAccountType() && (transfer.getMemberNo() != null))
			if (!payerAccountVO.getMemberNo().equals(
					transfer.getMemberNo()))
				throw new AccountServiceException(
						AccountResponseCode.ACCOUNT_ACCOUNTNO_NOT_EQUALS_MEMBERNO_ERROR,
						"payer accountNo do not belong to " + transfer.getMemberNo());
		logger.debug("validate memeber account password");
		if (checkPsw)
			if (PasswordFlag.CHECK == payerAccountVO.getAccountPasswdFlag())
				if (!payerAccountVO.getAccountPasswd().equals(
						transfer.getAccountPasswd()))
					throw new AccountServiceException(
							AccountResponseCode.ACCOUNT_PSW_ERROR,
							"account password is wrong");
		
		//校验付款方余额
		if(validateBalance(payerAccountVO,transfer.getAmount(),DrcrFlag.DR,payerAccountVO.getOverdraftAmountLimit())){
			throw new AccountServiceException(AccountResponseCode.ACCT_POSTING_ACCT_Bal_NOTENOUGH,"acct posting acct balance not enough:accountNo["+payerAccountVO.getAccountNo()+"]");
		}
		
	}
	
	
	
	/**
	 * 校验付款方余额
	 * @param accountVO
	 * @param drCrAmount
	 * @param drcrFlag
	 * @param overdraftAmountLimit
	 * @return
	 */
	private boolean validateBalance(AccountVO accountVO,Long drCrAmount,DrcrFlag drcrFlag,Long overdraftAmountLimit){
		Long ueablebalanceAmount = 0L;
		if(drcrFlag == accountVO.getSubjectDrCr()){
			ueablebalanceAmount = accountVO.getCashAmount() + drCrAmount + overdraftAmountLimit;
		}else{
			ueablebalanceAmount = accountVO.getCashAmount() - drCrAmount + overdraftAmountLimit;
		}
		if(ueablebalanceAmount < 0){
			return true;
		}else{
			return false;
		}
		
	}
	
	/**
	 * 账户状态校验
	 * @param accountVO
	 * @param drcrFlag
	 * @throws AccountServiceException
	 */
	
	private void accoutStatusVValidator(AccountVO accountVO, DrcrFlag drcrFlag)
			throws AccountServiceException {

		// 校验激活状态
		if (AccountActive.UN_ACTIVE == accountVO.getAccountActive()) {
			throw new AccountServiceException(
					AccountResponseCode.ACCT_POSTING_ACCT_UN_ACTIVE,
					"acct posting acct unactive:accountNo["
							+ accountVO.getAccountNo() + "]");
		}
		// 校验冻结状态
		if (FreezeState.FREEZE == accountVO.getFreezeState()) {
			throw new AccountServiceException(
					AccountResponseCode.ACCT_POSTING_ACCT_FREEZE,
					"acct posting acct freeze:accountNo["
							+ accountVO.getAccountNo() + "]");
		}
		// 校验账户状态
		AccountState accountState = accountVO.getAccountState();
		// AccState
		if (AccountState.CANCEL == accountState) {
			throw new AccountServiceException(
					AccountResponseCode.ACCT_POSTING_ACCT_CANCEL,
					"acct posting acct cancel:accountNo["
							+ accountVO.getAccountNo() + "]");
		}

		// 禁止借方交易
		if (AccountState.IN_FORBIDEN == accountState && drcrFlag == DrcrFlag.CR) {
			throw new AccountServiceException(
					AccountResponseCode.ACCT_POSTING_ACCT_IN_FORBIDEN,
					"acct posting acct inforbiden:accountNo["
							+ accountVO.getAccountNo() + "]");
		}
		// 禁止贷方交易
		if (AccountState.OUT_FORBIDEN == accountState
				&& drcrFlag == DrcrFlag.DR) {
			throw new AccountServiceException(
					AccountResponseCode.ACCT_POSTING_ACCT_OUT_FORBIDEN,
					"acct posting acct outforbiden:accountNo["
							+ accountVO.getAccountNo() + "]");
		}

	}
	
	
	/**
	 * 参数校验
	 *
	 * 方法说明：
	 *
	 * @param transfer
	 *
	 * @author 299015 彭阳
	 */
	private void validatorParam(Transfer transfer) {
		AssertUtils.notNull(transfer);
		AssertUtils.notNullAndEmpty(transfer.getPaymentOrderNo() , "paymentNo can not be null");
		AssertUtils.notNullAndEmpty(transfer.getTallySource(), "tally source can not be null");
		AssertUtils.notNullAndEmpty(transfer.getTallyType(), "tally type can not be null");
		//AssertUtils.notNullAndEmpty(transfer.getMemberNo());
		AssertUtils.notNullAndEmpty(transfer.getTransOrderNo(), "orderNo can not be null");
		AssertUtils.notNullAndEmpty(transfer.getOrderNo(), "order tally no can not be null");
		AssertUtils.notNullAndEmpty(transfer.getPaymentNo(), "payment tally no can not be null");
		AssertUtils.notNullAndEmpty(transfer.getPayerAccount(), "payer account can not be null");
		AssertUtils.notNullAndEmpty(transfer.getPayeeAccount(), "payee account can not be null");
		AssertUtils.notNullAndEmpty(transfer.getExpenseFlag(), "expense type can not be null");
		AssertUtils.notNullAndEmpty(transfer.getAmount(), "amount can not be null");
		if(transfer.getOperatorId() == null){
			transfer.setOperatorId(0L);
		}
		//AssertUtils.notNullAndEmpty(transfer.getOperatorId());
	}


}
